<!-- 虚拟表格，但没封装好 -->
<template>
  <div class="tableV2X" ref="tableFatherDom" :class="{haveBorder: props.border}">
    <el-auto-resizer>
      <template #default="{ height, width }">
        <el-table-v2 class="tableV2XContent" ref="tableV2Dom"
                     :width="width" :height="height"
                     :scrollbar-always-on="scrollAlways"
                     :row-key="idKey"
                     :expand-column-key="treeProps.treeField"
                     :columns="tableField" :data="tableData"
                     v-bind="$attrs">

          <template #header-cell="{column,columnIndex,columns,headerIndex,style}">
            <div class="headerCellBox" :style="column.headStyle">
              <!-- 虚拟table树形结构未展开的数据是未加载的，这时候全选不好处理 -->
              <!--              <div class="headerCell" v-if="column.isSelection">-->
              <!--                <el-checkbox class="myCheck" v-model="checkAllStatus"-->
              <!--                             :indeterminate="checkAllStatusWait"-->
              <!--                             size="large" @change="chooseAllFn"/>-->
              <!--              </div>-->

              <div class="headerCell" :style="column.headStyle">
                {{ labelI18Fn(column, columnIndex) }}
              </div>
            </div>
          </template>

          <template #cell="{column,columnIndex,rowIndex,style,rowData}">
            <div class="cellBox" id="tableV2XCellBox" :style="column.dataStyle">

              <!-- region 单元格表格，信息 -->
              <div id="eventBox" style="display: none">
                <span class="columnProp">{{ column.prop }}</span>
                <span class="columnSlot">{{ column.slot }}</span>
                <span class="dataKey">{{ rowData[idKey] }}</span>
                <span class="setKey">{{ setKeyValue(rowData) }}</span>
              </div>
              <!-- endregion -->

              <!-- region 插槽 -->
              <div class="cellContent" v-if="column.slot">
                <slot :name="column.slot" :column="column"
                      :row="rowData" :$index="rowIndex"
                      :columnIndex="columnIndex"/>
              </div>

              <!-- endregion -->

              <!-- region 序号 -->
              <div class="isIndex cellContent" v-else-if="column.isIndex">
                {{ rowIndex + 1 }}
              </div>
              <!-- endregion -->

              <!-- region 勾选 -->
              <div class="isSelect cellContent" v-else-if="column.isSelection && rowData[idKey]">
                <el-checkbox class="myCheck" v-model="nowChooseKey[rowData[idKey]]"
                             size="large" @change="chooseFn($event,rowData)"/>
              </div>
              <!-- endregion -->

              <!-- region 回调函数 -->
              <div class="cellContent" v-else-if="typeof column.html == 'function'">
                <span v-html="column.html(rowData,column,rowIndex,props.callBackQuery)"></span>
              </div>
              <div class="cellContent" v-else-if="typeof column.callBack == 'function'">
                {{ column.callBack(rowData, column, rowIndex, props.callBackQuery) }}
              </div>
              <!-- endregion -->

              <!-- region 普通值 -->
              <div class="cellContent">
                <span>{{ rowData[column.prop] }}</span>
              </div>
              <!-- endregion -->

            </div>
          </template>

        </el-table-v2>
      </template>
    </el-auto-resizer>
  </div>
</template>

<script setup>
import {ref, nextTick, watch, computed,onMounted,onBeforeUnmount} from 'vue'
import {WatchField} from 'xhyk-tools/vueSetup.js'
import { ElAutoResizer,ElTableV2,ElCheckbox } from 'element-plus'


const props = defineProps({
  // 表格双向绑定
  modelValue: {
    type: Object,
    default: []
  },

  tableData: {
    type: Object,
    default: []
  },

  tableTitle: {
    type: Object,
    default: []
  },

  callBackQuery: {
    type: Object,
    default: {}
  },


  // 唯一key。为了防止后端返回的id不是唯一，可以设置多个字段值拼接，如：['id','code'] => `${id}_${code}`
  rowKey: {
    type: [Object, String],
    default: ['id', 'code', 'level']
  },

  border: {
    type: Boolean,
    default: true
  },

  treeProps: {
    type: Object,
    default: {
      // 树形结构掌控的字段名
      // treeField: 'code'
    }
  },

  // 滚动条设置
  scrollStyle: {
    type: Object,
    default: {
      // always: true, // 可以显示滚动条时，总是显示滚动条
      // width: '14px', // 两条滚动条宽带。接受Number或string
      // widthX: '14px', // 横向滚动条宽带。下面值都有总体和XY区分（XY权限优先）
      // color: '#909399',  // 两条滚动条颜色
      // opacity: 0.7, // 滚动条透明度
      // translate: '2px', // 偏移量
      // show: true,// 不写或true为显示两条滚动条，否则隐藏
    }
  },

  // 国际化函数
  i18Fn: {
    type: Function,
    // row：如果是头部，则为空的{}。tableTitleItem：为字段数据。which：'label'为头部，'content'为内容数据（此时第一个参数为数据row）
    default: (row = {}, tableTitleItem = {}, which = 'content') => {
      if (which == 'label') {
        return tableTitleItem.label
      } else {
        if (tableTitleItem.prop) {
          return row[tableTitleItem.prop]
        } else {
          return ''
        }
      }
    }
  },

  // 是否关联下级的勾选
  noClick: {
    type: Boolean,
    default: false
  },

  // 点击行时，对多选框的操作
  selectChange: {
    type: [Number, String],
    // type： -1：不做任何操作。0：只会取消，1：只会勾选。2：反选
    default: -1
  },

})

const emit = defineEmits(['update:modelValue', 'cellClick', 'rowClick', 'selectX'])

// region 工具函数

// 获取id
function getMid(len = 30) {
  let result = Date.now().toString();
  while (result.length < len) {
    let temp = Math.random().toString(36)
    if (temp.length > 2) {
      temp = temp.slice(2, temp.length)
    } else {
      temp = "xhyk"
    }
    result = `${temp}${result}`
  }

  if (result.length > len) {
    let index = result.length - len
    result = result.slice(index, result.length)
  }

  return result
}

// 判断prop传参的值。接受[String,Number]的style样式尺寸
function getPx(text, defaultPx = 'px') {
  let tempList = ['px', 'rpx', 'vw', 'vh', '%', 'em', 'auto']
  if (typeof text == 'string') {
    const havePx = tempList.find(row => text.includes(row))
    if (havePx) {
      return text
    } else {
      return `${parseFloat(text)}${defaultPx}`
    }
  } else if (typeof text == 'number') {
    let temp = parseFloat(text)
    return `${temp}${defaultPx}`
  }
  return text
}


// 遍历嵌套取值。根据children字段遍历下级列表。返回当前子级对象
async function findChildren(data = [], callBack = undefined, children = "children") {
  let tempResult = undefined
  const findFn = async (ChildData = [], father) => {
    let isOk = false
    for (let i = 0; i < ChildData.length; i++) {
      const item = ChildData[i]
      if (typeof callBack == "function") {
        const isBreak = await callBack(item, father)
        if (isBreak == true) {
          tempResult = item
          isOk = true
          break;
        }
      }
      if (isOk) {
        tempResult = item
        return item
      }

      // 如果有下一级数据
      if (item[children] != undefined && item[children].length > 0) {
        let result = await findFn(item[children], item)
        if (result) {
          return true
        }
      }
    }
    return isOk;
  }
  await findFn(data, undefined)
  return tempResult
}

// 字符串或数组去重复元素
function removeSame(Obj) {
  if (typeof Obj === 'string') {
    const text = new Set(Obj)
    return [...text].join('')
  } else if (Obj instanceof Array) {
    return Array.from(new Set(Obj))
  } else {
    return Obj
  }
}

// endregion

// 表格组件对象
const tableV2Dom = ref({})

const watchObj = new WatchField(emit, props)


// region 滚动条样式

const scrollWidthX = ref('')
const scrollWidthY = ref('')
const scrollTranslateX = ref('')
const scrollTranslateY = ref('')
const scrollColorX = ref('')
const scrollColorY = ref('')
const scrollDisplayX = ref('')
const scrollDisplayY = ref('')
const scrollOpacityX = ref(0.7)
const scrollOpacityY = ref(0.7)
const scrollAlways = ref(false)

watch(() => props.scrollStyle, (newV, oldV) => {

  // region 宽度
  let width = newV.width == undefined ? '10px' : newV.width;
  scrollWidthX.value = getPx(width)
  scrollWidthY.value = getPx(width)
  if (newV.widthX != undefined) {
    scrollWidthX.value = getPx(newV.widthX)
  }
  if (newV.widthY != undefined) {
    scrollWidthY.value = getPx(newV.widthY)
  }
  // endregion

  // region 偏移量
  let translate = newV.translate == undefined ? '3px' : newV.translate;
  scrollTranslateX.value = getPx(translate)
  scrollTranslateY.value = getPx(translate)
  if (newV.translateX != undefined) {
    scrollTranslateX.value = getPx(newV.translateX)
  }
  if (newV.translateY != undefined) {
    scrollTranslateY.value = getPx(newV.translateY)
  }

  // endregion

  // region 颜色
  let color = newV.color == undefined ? '#909399' : newV.color
  scrollColorX.value = color
  scrollColorY.value = color
  if (newV.colorX != undefined) {
    scrollColorX.value = newV.colorX
  }
  if (newV.colorY != undefined) {
    scrollColorY.value = newV.colorY
  }
  // endregion

  // region 透明度
  let opacity = newV.opacity == undefined ? 0.7 : newV.opacity
  scrollOpacityX.value = opacity
  scrollOpacityY.value = opacity
  if (newV.opacityX != undefined) {
    scrollOpacityX.value = newV.opacityX
  }
  if (newV.opacityY != undefined) {
    scrollOpacityY.value = newV.opacityY
  }
  // endregion

  // region 显示
  let show = (newV.show == undefined || newV.show == true) ? 'block' : 'none'
  scrollDisplayX.value = show
  scrollDisplayY.value = show
  if (newV.showX != undefined) {
    scrollDisplayX.value = (newV.showX == undefined || newV.showX == true) ? 'block' : 'none'
  }
  if (newV.showY != undefined) {
    scrollDisplayY.value = (newV.showY == undefined || newV.showY == true) ? 'block' : 'none'
  }
  // endregion

  // region 总是显示
  scrollAlways.value = (newV.always == undefined || newV.always == true) ? true : newV.always
  // endregion

}, {immediate: true, deep: true})

// endregion


// region 表格字段

const tableField = ref([])

const setTitle = () => {
  let result = []
  props.tableTitle?.forEach((item, index) => {

    item.key = item.prop || item.label

    item.title = item.label || item.prop

    item.align = item.align == undefined ? 'center' : item.align


    if (item.width != undefined) {
      item.width = parseFloat(item.width) || 0
    }
    if (item.minWidth != undefined) {
      item.minWidth = parseFloat(item.minWidth) || 0
    }

    if (item.i18Fn == undefined) {
      item.i18Fn = props.i18Fn
    }

    // item.fixed = undefined
    if (typeof item.fixed != 'string') {
      item.fixed = undefined
    }

    if (item.type == 'selection') {
      item.isSelection = true
    } else {
      item.isSelection = false
    }

    if (item.type == 'index') {
      item.isIndex = true
    } else {
      item.isIndex = false
    }

    if (item.show != false) {
      result.push(item)
    }
  })
  tableField.value = result
}

watchObj.watchLast(() => props.tableTitle, (newV) => {
  nextTick(() => {
    setTitle()
  })
}, {deep: false})


// endregion


// region 树形结构数据

const treeProps = computed(() => {
  let result = {hasChildren: 'hasChildren', children: 'children', treeField: 'code'}
  Object.assign(result, props.treeProps)
  return result
})

// endregion


// region 单元格被点击

// 数据内容的单元格被点击事件
const clickDataCellFn = (e) => {
  // 判断当前dom是否为父级dom
  let thisDom = e.target
  if (typeof thisDom.className != 'string') {
    return
  }
  let number = 0
  while (thisDom.className.indexOf('el-table-v2__row-cell') == -1) {
    thisDom = thisDom.parentNode
    number++
    if (number > 100) {
      break
    }
  }

  // 获取属性dom
  const infoDom = thisDom?.querySelector('#eventBox')
  if (infoDom) {
    const columnProp = infoDom.querySelector('.columnProp')?.innerText || ''
    const columnSlot = infoDom.querySelector('.columnSlot')?.innerText || ''
    const dataKey = infoDom.querySelector('.dataKey')?.innerText || ''

    const fieldItem = tableField.value.find(row => {
      if (columnProp) {
        return row.prop == columnProp
      } else {
        return row.slot == columnSlot
      }
    })

    const data = levelData.value.find(row => row[idKey.value] == dataKey)

    switch (parseFloat(props.selectChange)) {
      case -1:
        break
      case 0:
        nowChooseKey.value[data[idKey.value]] = false
        nowCheck.value = nowCheck.value.filter(row => row[idKey.value] != data[idKey.value])
        break;
      case 1:
        nowChooseKey.value[data[idKey.value]] = true
        const havePush = nowCheck.value.find(row => row[idKey.value] == data[idKey.value])
        if (havePush == undefined) {
          nowCheck.value.push(data)
        }
        break
      case 2:
        if (nowChooseKey.value[data[idKey.value]] == true) {
          nowChooseKey.value[data[idKey.value]] = false
          nowCheck.value = nowCheck.value.filter(row => row[idKey.value] != data[idKey.value])
        } else {
          nowChooseKey.value[data[idKey.value]] = true
          const havePush = nowCheck.value.find(row => row[idKey.value] == data[idKey.value])
          if (havePush == undefined) {
            nowCheck.value.push(data)
          }
        }
        break
    }

    emit('update:modelValue', nowCheck.value)
    emit('rowClick', {row: data, column: fieldItem})
  }
}


// 添加事件
const setClickEvent = () => {
  nextTick(() => {
    if (tableFatherDom.value){
      const allDataCell = tableFatherDom.value.querySelectorAll('div.el-table-v2__row-cell')
      allDataCell?.forEach(item => {
        item.removeEventListener('click', clickDataCellFn)
        item.addEventListener('click', clickDataCellFn)
      })
    }
  })
}

// endregion


// region 处理数据

const idKey = ref('tableV2XKey')

// 设置唯一key对应的值
const setKeyValue = (item) => {
  let idList = []
  if (typeof props.rowKey == 'string') {
    idList = [props.rowKey]
  } else {
    // ['id','code'] => `${id}_${code}`
    idList = props.rowKey
  }

  let idValue = ""
  idList.forEach(key => {
    idValue = `${idValue}_${item[key]}`
  })
  item[idKey.value] = idValue
  levelData.value.push(item)
  return idValue
}

// 表格数据
const tableData = ref([])

// 表格的一层数据
const levelData = ref([])

watchObj.watchLast(() => props.tableData, async (newV) => {
  isWatchCheck.value = false
  levelData.value = []
  tableData.value = []
  tableData.value = newV
  setClickEvent()
  setCheckFn()
})

// endregion


// region 勾选

// 全选状态
const checkAllStatus = ref(false)

// 全选的等待状态
const checkAllStatusWait = computed(() => {
  let lenght = 0
  for (let key in nowChooseKey.value) {
    if (nowChooseKey.value[key] == true) {
      lenght += 1
    }
  }
  if (lenght >= levelData.value.length && lenght > 0) {
    checkAllStatus.value = true
  }

  return lenght < levelData.value.length && lenght > 0
})

// 当前选择的key的集合
const nowChooseKey = ref({})

// 当前勾选列表
const nowCheck = ref([])

// 是否开始监听
const isWatchCheck = ref(false)

const setCheckFn = () => {
  isWatchCheck.value = false
  nowCheck.value = props.modelValue || []
  // 不直接赋值是因为 如果直接赋值，会触发勾选框不好立刻刷新
  for (let key in nowChooseKey.value) {
    nowChooseKey.value[key] = false
  }
  props.modelValue.forEach((item, index) => {
    item[idKey.value] = setKeyValue(item)
    const id = item[idKey.value]
    nowChooseKey.value[id] = true
    const haveCheck = levelData.value.find(row => row[idKey.value] == item[idKey.value])
    if (haveCheck) {
      nowCheck.value[index] = haveCheck
    }
  })
  emit('update:modelValue', nowCheck.value)
  isWatchCheck.value = true
}

// 监听双向绑定
watch(() => props.modelValue, (newV, oldV) => {
  const newList = JSON.parse(JSON.stringify(newV))
  let isUpdate = false
  if (nowCheck.value.length != props.modelValue.length) {
    isUpdate = true
  } else {
    // 数量相同
    newList.find((newItem) => {
      // 查找这个是不是不在旧列表中
      let idValue = setKeyValue(newItem)
      const have = nowCheck.value.find((oldItem) => {
        let oldIdValue = setKeyValue(oldItem)
        if (idValue == oldIdValue) {
          return true
        }
      })
      // 不在旧列表中
      if (!have) {
        isUpdate = true
        return true
      }
    })
  }
  // 是更新而且不处于watch监听props.tableData的时候
  if (isWatchCheck.value && isUpdate) {
    setCheckFn()
  }
}, {immediate: true, deep: true})


// 勾选全部
const chooseAllFn = (isChoose) => {
  isWatchCheck.value = false
  levelData.value.forEach(item => {
    nowChooseKey.value[item[idKey.value]] = isChoose
    if (isChoose) {
      const isNew = nowCheck.value.find(row => row[idKey.value] == item[idKey.value])
      if (!isNew) {
        nowCheck.value.push(item)
      }
    } else {
      nowCheck.value = nowCheck.value.filter(row => row[idKey.value] != item[idKey.value])
    }
  })
  isWatchCheck.value = true
  emit('update:modelValue', nowCheck.value)
}

// 勾选当前
const chooseFn = async (isChoose, item) => {
  nowChooseKey.value[item[idKey.value]] = isChoose
  isWatchCheck.value = false
  if (isChoose) {
    const isNew = nowCheck.value.find(row => row[idKey.value] == item[idKey.value])
    if (!isNew) {
      nowCheck.value.push(item)
    }
  } else {
    nowCheck.value = nowCheck.value.filter(row => row[idKey.value] != item[idKey.value])
  }

  // 进行关联子级的关联
  if (!props.noClick) {
    const childrenList = item.children || []
    await findChildren(childrenList, (child) => {
      if (isChoose) {
        const isNew = nowCheck.value.find(row => row[idKey.value] == child[idKey.value])
        if (!isNew) {
          nowCheck.value.push(child)
          nowChooseKey.value[child[idKey.value]] = true
        }
      } else {
        nowChooseKey.value[child[idKey.value]] = false
        nowCheck.value = nowCheck.value.filter(row => row[idKey.value] != child[idKey.value])
      }
    }, treeProps.value.children || 'children')
  }
  isWatchCheck.value = true
  emit('update:modelValue', nowCheck.value)
}

// endregion


// region 国际化
// 表头字段的国际化处理
const labelI18Fn = (column, $index) => {
  return props.i18Fn({}, column, 'label')
}

// region 例子
// const getTitleFn = (row, titleItem, which) => {
//   if (which == 'label') {
//     if (titleItem.i18Text) {
//       return var_i18n_getText(titleItem.i18Text)
//     }
//     return titleItem.label
//   } else {
//     if (titleItem.prop) {
//       // 数据国际化显示
//       if (row.i18Text && titleItem.prop == 'reqApiType') {
//         return var_i18n_getText(row.i18Text)
//       }
//       return row[titleItem.prop]
//     }
//     return ''
//   }
// }
// endregion

// endregion


// region 表格宽高

const tableFatherDom = ref({})
const widthD = ref(0)

const heightD = ref(0)

const updateWH = () => {
  nextTick(() => {
    if (tableFatherDom.value && tableFatherDom.value.nodeType == Node.ELEMENT_NODE) {
      const width = window.getComputedStyle(tableFatherDom.value).width
      widthD.value = parseFloat(width)

      const height = window.getComputedStyle(tableFatherDom.value).height
      heightD.value = parseFloat(height)

    } else {
      widthD.value = 0
      heightD.value = 0
    }
  })
}

onMounted(() => {
  updateWH()
  window.addEventListener('resize', () => {
    updateWH()
  })
})


onBeforeUnmount(() => {
  window.removeEventListener('resize', () => {
    updateWH()
  })
})

// endregion


</script>

<style scoped lang='scss'>
.tableV2X {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  top: 0;

  .tableV2XContent {
    width: 100%;
    height: 100%;
  }
}

// region 需要边框
.haveBorder {
  border: 1px solid #dddddd;

  :deep(.el-table-v2__header-cell) {
    border-left: 1px solid #dddddd;
  }

  :deep(.el-table-v2__row-cell) {
    border-left: 1px solid #dddddd;
  }
}

// endregion


// region 勾选框大小
:deep(.el-table-column--selection) {
  .el-checkbox__inner {
    width: 16px;
    height: 16px;

    &:before {
      height: 6px;
    }

    &:after {
      left: 5px;
      top: 0px;
      height: 9px;
      width: 4px;
      border: 2px solid white;
      border-left: 0;
      border-top: 0;
      //transform: rotate(45deg) scaleY(1);
    }
  }
}

:deep(.myCheck) {
  .el-checkbox__inner {
    width: 16px;
    height: 16px;

    &:before {
      height: 6px;
    }

    &:after {
      left: 5px;
      top: 0px;
      height: 9px;
      width: 4px;
      border: 2px solid white;
      border-left: 0;
      border-top: 0;
      //transform: rotate(45deg) scaleY(1);
    }
  }
}

// endregion

// region 内容宽度
.cellBox {
  width: 100%;
}

// endregion


// region 滚动条样式

// 滚动条样式
@mixin v2ScrollStyle() {

  // 横向滚动条
  :deep(.el-vl__horizontal) {
    bottom: v-bind(scrollWidthY) !important;
    transform: translateY(v-bind(scrollTranslateY));

    .el-scrollbar__thumb {
      height: v-bind(scrollWidthY) !important;
      background-color: v-bind(scrollColorY);
      opacity: v-bind(scrollOpacityY);
      display: v-bind(scrollDisplayY);
    }
  }

  // 纵向滚动条
  :deep(.el-vl__vertical) {
    right: v-bind(scrollWidthX) !important;
    transform: translateX(v-bind(scrollTranslateY));

    .el-scrollbar__thumb {
      width: v-bind(scrollWidthX) !important;
      background-color: v-bind(scrollColorX);
      opacity: v-bind(scrollOpacityX);
      display: v-bind(scrollDisplayX);
    }
  }
}

.tableV2X {
  @include v2ScrollStyle();
}


// endregion


</style>